package se.webbzon.boltzmann.camera;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import se.webbzon.boltzmann.debug.Debug;
import se.webbzon.oschi01.worldcamera.WorldView;
import se.webbzon.oschi01.worldobject.WorldObject;

public abstract class AbstractCamera extends WorldObject implements Camera {
	
	// A collection of all the views displaying the content
	// of this camera
	private final Collection<WorldView> views;
	
	// A collection of all the listeners to this camera
	private final Collection<CameraListener> listeners;
	
	// The rotation and distance of the camera
	private double rX, rY, rZ, distance;
	
	/*============================================================
	Constructors
	============================================================*/
	
	/** Creates a new abstract camera which controls the provided
	 * world camera. **/
	public AbstractCamera(int registeredMethods) {
		super(registeredMethods);
		views = Collections.synchronizedCollection(new ArrayList<WorldView>());
		listeners = new ArrayList<CameraListener>();
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	@Override public double getRotationX() {
		return rX;
	}
	
	@Override public double getRotationY() {
		return rY;
	}
	
	@Override public double getRotationZ() {
		return rZ;
	}
	
	@Override public double getDistance() {
		return distance;
	}
	
	/** Adds a listener to this camera. **/
	public boolean addListener(CameraListener listener) {
		int d;
		Debug.debugCollection(this, listeners);
		return listeners.add(listener);
	}
	
	/** Removes a listener from this camera. **/
	public boolean removeListener(CameraListener listener) {
		return listeners.remove(listener);
	}
	
	/** Clears all the listeners from this camera. **/
	public void clearListeners() {
		listeners.clear();
	}
	
	@Override public boolean addView(WorldView view) {
		if (views.add(view)) {
			int d;
			Debug.debugCollection(this, views);
			for (CameraListener listener : listeners.toArray(new CameraListener[0]))
				listener.onViewAdded(this,view);
			return true;
		} else
			return false;
	}
	
	@Override public boolean removeView(WorldView view) {
		if (views.remove(view)) {
			for (CameraListener listener : listeners.toArray(new CameraListener[0]))
				listener.onViewRemoved(this,view);
			return true;
		} else
			return false;
	}
	
	@Override public void clearViews() {
		views.clear();
		for (CameraListener listener : listeners.toArray(new CameraListener[0]))
			listener.onViewsCleared(this);
	}
	
	@Override public Collection<WorldView> getViews() {
		return Collections.unmodifiableCollection(views);
	}
	
	/** Sets the projection of the camera to a perspective projection
	 * from a given coordinate to a given coordinate with the specified
	 * up vector. **/
	public void setProjectionPerspective(Point3d from, Point3d to, Vector3d up) {
		rX = Math.atan2(to.getZ() - from.getZ(), to.getY() - from.getY());
		rY = Math.atan2(to.getX() - from.getX(), to.getZ() - from.getZ());
		rZ = Math.atan2(to.getY() - from.getY(), to.getX() - from.getX());
		distance = from.distance(to);
		synchronized (views) {
			for (WorldView view : views)
				view.getCamera().projectPerspective(from, to, up);
		}
	}

}
